﻿using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using CVS.DAL.Models;

namespace CVS.DAL
{
    public static class DataRepository
    {
        public static int AddSubmission( Submission submission )
        {
            using ( var context = new CodeVerificationSystemEntities( ) )
            {
                var addedSubmission = context.Submissions.Add( submission );
                context.SaveChanges( );

                return addedSubmission.Id;
            }
        }

        public static List< Submission > GetSubmissions( int taskId )
        {
            using (var context = new CodeVerificationSystemEntities())
            {
                return context.Submissions.Where(
                    s =>
                        s.TaskId == taskId )
                    .Include( s => s.CompilationResult )
                    .Include( s => s.TestRuns ).ToList( );
            }
        }

        public static List<Submission> GetSubmissions(int taskId, string submittedBy)
        {
            using (var context = new CodeVerificationSystemEntities())
            {
                return context.Submissions.Where(
                    s => s.TaskId == taskId && s.SubmittedBy == submittedBy)
                    .Include(s => s.CompilationResult)
                    .Include(s => s.TestRuns).ToList();
            }
        }

        public static List<Submission> GetPendingSubmissions()
        {
            using (var context = new CodeVerificationSystemEntities())
            {
                var fiveSecondsAgo = DateTime.Now.AddSeconds(-5);
                return
                    context.Submissions.Where(
                        s =>
                            (!s.IsProcessed.HasValue || s.IsProcessed == false) &&
                            (s.ProcessingStartTime == null || s.ProcessingStartTime < fiveSecondsAgo))
                                          .Include(s => s.CompilationResult)
                                          .Include(s => s.TestRuns).ToList();
            }
        }

        public static void UpdateSubmission( Submission submission )
        {
            using ( var context = new CodeVerificationSystemEntities( ) )
            {
                context.Submissions.Attach( submission );
                context.Entry( submission ).State = EntityState.Modified;
                context.SaveChanges( );
            }
        }

        public static List<TestDefinition> GetTestDefinitionsForSubmission(Submission submission)
        {
            using (var context = new CodeVerificationSystemEntities())
            {
                return context.TestDefinitions.Where(t => t.TaskId == submission.TaskId).ToList();
            }
        }

        public static CompilationResult AddCompilationResult(CompilationResult compilationResult)
        {
            using (var context = new CodeVerificationSystemEntities())
            {
                var result = context.CompilationResults.Add(compilationResult);
                context.SaveChanges();
                return result;
            }
        }

        public static SubmissionDetails GetSubmissionDetails( int submissionId )
        {
            using (var context = new CodeVerificationSystemEntities())
            {
                var submission = context.Submissions.Single(x => x.Id == submissionId);
                var compilationResult = submission.CompilationResult;
                var submissionDetails = new SubmissionDetails
                {
                    SourceCode = submission.SourceCode,
                    CompilationSuccessful = compilationResult.IsSuccessful,
                    CompilationOutput = compilationResult.Output,
                    ExecutedTestRuns = new List< ExecutedTestRun >( )
                };
                var testRuns = submission.TestRuns;
                foreach (var testRun in testRuns)
                {
                    submissionDetails.ExecutedTestRuns.Add( new ExecutedTestRun
                    {
                        Input = testRun.TestDefinition.Input,
                        ExpectedOutput = testRun.TestDefinition.ExpectedOutput,
                        ActualOutput = testRun.Output,
                        TestRunSuccessful = testRun.IsSuccessful
                    });
                }

                return submissionDetails;
            }
        }

        public static void AddTestRuns(List<TestRun> testRuns)
        {
            using (var context = new CodeVerificationSystemEntities())
            {
                foreach (var testRun in testRuns)
                {
                    context.TestRuns.Add(testRun);
                }

                context.SaveChanges();
            }
        }

        public static int AddTask(Task task)
        {
            using (var context = new CodeVerificationSystemEntities())
            {
                var addedTask = context.Tasks.Add(task);
                context.SaveChanges();

                return addedTask.Id;
            }
        }

        public static List< Task > GetTasks( )
        {
            using (var context = new CodeVerificationSystemEntities())
            {
                return context.Tasks.ToList( );
            }
        }

        public static void DeleteTask(Task task)
        {
            using (var context = new CodeVerificationSystemEntities())
            {
                context.Tasks.Attach(task);
                context.Entry(task).State = EntityState.Deleted;
                context.SaveChanges();
            }
        }

        public static List< TestDefinition > GetTestsForTask( int taskId )
        {
            using (var context = new CodeVerificationSystemEntities())
            {
                return context.TestDefinitions.Where( t => t.TaskId == taskId ).ToList( );
            }
        }

        public static void AddTest(TestDefinition test)
        {
            using (var context = new CodeVerificationSystemEntities())
            {
                context.TestDefinitions.Add(test);
                context.SaveChanges();
            }
        }

        public static void DeleteTest(TestDefinition test)
        {
            using (var context = new CodeVerificationSystemEntities())
            {
                context.TestDefinitions.Attach(test);
                context.Entry(test).State = EntityState.Deleted;
                context.SaveChanges();
            }
        }
    }
}
